/*
 * svg_parsing.h
 *
 *  Created on: Jul 31, 2012
 *      Author: jduo
 */

#ifndef SVG_PARSING_H_
#define SVG_PARSING_H_

namespace svg_parsing{

  /// Defines storage for a LIFO collection of transformation matrices.
  typedef std::stack<k3d::matrix4> transform_stack;

  /// Helper function for parse_path to extract a pair (x,y) regardless of the Format i.e. "x,y" or "x y".
  static void get_pair(std::istream& Stream, k3d::double_t& X, k3d::double_t& Y)
  {
          char separator;
          Stream >> X >> separator >> Y;
  }

  /// Tries to find the color of the given element. Returns true if a color was found
  k3d::bool_t get_color(const k3d::xml::element& SVG, k3d::color& Color)
  {
          k3d::string_t style = k3d::xml::attribute_text(SVG, "style");
          k3d::mesh::strings_t styles;
          boost::split(styles, style, boost::is_any_of(";"));
          for(k3d::uint_t i = 0; i != styles.size(); ++i)
          {
                  if(boost::algorithm::starts_with(styles[i], "stroke:"))
                  {
                          k3d::string_t stroke = styles[i].substr(8,6);
                          std::stringstream stream;
                          k3d::uint32_t red, green, blue;
                          stream << "0x000000" << stroke.substr(0, 2);
                          stream >> std::hex >> red;
                          stream.clear();
                          stream << "0x000000" << stroke.substr(2, 2);
                          stream >> std::hex >> green;
                          stream.clear();
                          stream << "0x000000" << stroke.substr(4, 2);
                          stream >> std::hex >> blue;
                          Color = k3d::color(static_cast<k3d::double_t>(red)/255.0, static_cast<k3d::double_t>(green)/255.0, static_cast<k3d::double_t>(blue)/255.0);
                          return true;
                  }
          }
          return false;
  }

  static void parse_line(const k3d::xml::element& SVG, transform_stack& Transformation, k3d::mesh& Mesh, k3d::nurbs_curve::primitive& Primitive)
  {
          const k3d::double_t x1 = k3d::xml::attribute_value<k3d::double_t>(SVG, "x1", 0);
          const k3d::double_t y1 = k3d::xml::attribute_value<k3d::double_t>(SVG, "y1", 0);
          const k3d::double_t x2 = k3d::xml::attribute_value<k3d::double_t>(SVG, "x2", 1);
          const k3d::double_t y2 = k3d::xml::attribute_value<k3d::double_t>(SVG, "y2", 1);

          k3d::mesh::points_t control_points;
          control_points.push_back(Transformation.top() * k3d::point3(x1, y1, 0));
          control_points.push_back(Transformation.top() * k3d::point3(x2, y2, 0));

          k3d::nurbs_curve::add_curve(Mesh, Primitive, 2, control_points);
  }

  static void parse_rect(const k3d::xml::element& SVG, transform_stack& Transformation, k3d::mesh& Mesh, k3d::nurbs_curve::primitive& Primitive)
  {
          const k3d::double_t x = k3d::xml::attribute_value<k3d::double_t>(SVG, "x", 0);
          const k3d::double_t y = k3d::xml::attribute_value<k3d::double_t>(SVG, "y", 0);
          const k3d::double_t w = k3d::xml::attribute_value<k3d::double_t>(SVG, "width", 1);
          const k3d::double_t h = k3d::xml::attribute_value<k3d::double_t>(SVG, "height", 1);
          k3d::double_t rx = k3d::xml::attribute_value<k3d::double_t>(SVG, "rx", -1);
          k3d::double_t ry = k3d::xml::attribute_value<k3d::double_t>(SVG, "ry", -1);

          if(ry == -1)
                  ry = rx;
          else if(rx == -1)
                  rx = ry;

          if(rx <=0 && ry <= 0)
          {
                  k3d::mesh::points_t points;
                  points.push_back(Transformation.top() * k3d::point3(x, y, 0));
                  points.push_back(Transformation.top() * k3d::point3(x + w, y, 0));
                  points.push_back(Transformation.top() * k3d::point3(x + w, y + h, 0));
                  points.push_back(Transformation.top() * k3d::point3(x, y + h, 0));

                  k3d::mesh::orders_t orders(4, 2);
                  k3d::mesh::counts_t control_point_counts(4, 2);

                  k3d::mesh::indices_t control_points = boost::assign::list_of(0)(1)(1)(2)(2)(3)(3)(0);

                  k3d::nurbs_curve::add_curves(Mesh, Primitive, points, orders, control_point_counts, control_points);
          }
          else
          {

                  // Create a circular_arc in the positive quadrant and mirror points to generate corners ...
                  k3d::mesh::knots_t corner_knots;
                  k3d::mesh::weights_t corner_weights;
                  k3d::mesh::points_t corner_control_points;
                  k3d::nurbs_curve::circular_arc(k3d::vector3(rx, 0, 0), k3d::vector3(0, ry, 0), 0, k3d::pi() / 2, 1, corner_knots, corner_weights, corner_control_points);

                  // Setup curves ...
                  k3d::mesh::points_t points;
                  k3d::mesh::orders_t orders;
                  k3d::mesh::counts_t control_point_counts;
                  k3d::mesh::indices_t control_points;
                  k3d::mesh::weights_t control_point_weights;
                  k3d::mesh::knots_t knots;

                  // Upper left corner ...
                  orders.push_back(3);
                  control_point_counts.push_back(corner_control_points.size());
                  knots.insert(knots.end(), corner_knots.begin(), corner_knots.end());
                  for(k3d::uint_t i = 0; i != corner_control_points.size(); ++i)
                  {
                          control_points.push_back(points.size());
                          control_point_weights.push_back(corner_weights[i]);
                          points.push_back(Transformation.top() * k3d::point3(x + rx - corner_control_points[i][0], y + ry - corner_control_points[i][1], 0));
                  }

                  // Top side ...
                  orders.push_back(2);
                  control_point_counts.push_back(2);
                  k3d::nurbs_curve::add_open_uniform_knots(2, 2, knots);
                  control_points.push_back(control_points.back());
                  control_points.push_back(points.size());
                  control_point_weights.push_back(1);
                  control_point_weights.push_back(1);

                  // Upper right corner ...
                  orders.push_back(3);
                  control_point_counts.push_back(corner_control_points.size());
                  knots.insert(knots.end(), corner_knots.begin(), corner_knots.end());
                  for(k3d::uint_t i = corner_control_points.size(); i != 0; --i)
                  {
                          control_points.push_back(points.size());
                          control_point_weights.push_back(corner_weights[i-1]);
                          points.push_back(Transformation.top() * k3d::point3(x + w - rx + corner_control_points[i-1][0], y + ry - corner_control_points[i-1][1], 0));
                  }

                  // Right side ...
                  orders.push_back(2);
                  control_point_counts.push_back(2);
                  k3d::nurbs_curve::add_open_uniform_knots(2, 2, knots);
                  control_points.push_back(control_points.back());
                  control_points.push_back(points.size());
                  control_point_weights.push_back(1);
                  control_point_weights.push_back(1);

                  // Lower right corner
                  orders.push_back(3);
                  control_point_counts.push_back(corner_control_points.size());
                  knots.insert(knots.end(), corner_knots.begin(), corner_knots.end());
                  for(k3d::uint_t i = 0; i != corner_control_points.size(); ++i)
                  {
                          control_points.push_back(points.size());
                          control_point_weights.push_back(corner_weights[i]);
                          points.push_back(Transformation.top() * k3d::point3(x + w - rx + corner_control_points[i][0], y + h - ry + corner_control_points[i][1], 0));
                  }

                  // Bottom side ...
                  orders.push_back(2);
                  control_point_counts.push_back(2);
                  k3d::nurbs_curve::add_open_uniform_knots(2, 2, knots);
                  control_points.push_back(control_points.back());
                  control_points.push_back(points.size());
                  control_point_weights.push_back(1);
                  control_point_weights.push_back(1);

                  // Lower left corner ...
                  orders.push_back(3);
                  control_point_counts.push_back(corner_control_points.size());
                  knots.insert(knots.end(), corner_knots.begin(), corner_knots.end());
                  for(k3d::uint_t i = corner_control_points.size(); i != 0; --i)
                  {
                          control_points.push_back(points.size());
                          control_point_weights.push_back(corner_weights[i-1]);
                          points.push_back(Transformation.top() * k3d::point3(x + rx - corner_control_points[i-1][0], y + h - ry + corner_control_points[i-1][1], 0));
                  }

                  // Left side ...
                  orders.push_back(2);
                  control_point_counts.push_back(2);
                  k3d::nurbs_curve::add_open_uniform_knots(2, 2, knots);
                  control_points.push_back(control_points.back());
                  control_points.push_back(0);
                  control_point_weights.push_back(1);
                  control_point_weights.push_back(1);

                  k3d::nurbs_curve::add_curves(Mesh, Primitive, points, orders, control_point_counts, control_points, control_point_weights, knots);
          }
  }

  static void parse_circle(const k3d::xml::element& SVG, transform_stack& Transformation, k3d::mesh& Mesh, k3d::nurbs_curve::primitive& Primitive)
  {
          const k3d::double_t cx = k3d::xml::attribute_value<k3d::double_t>(SVG, "cx", 0);
          const k3d::double_t cy = k3d::xml::attribute_value<k3d::double_t>(SVG, "cy", 0);
          const k3d::double_t r = k3d::xml::attribute_value<k3d::double_t>(SVG, "r", 1);

          k3d::mesh::knots_t knots;
          k3d::mesh::weights_t weights;
          k3d::mesh::points_t control_points;
          k3d::nurbs_curve::circular_arc(k3d::vector3(r, 0, 0), k3d::vector3(0, r, 0), 0, k3d::pi_times_2(), 4, knots, weights, control_points);

          for(k3d::uint_t i = 0; i != control_points.size(); ++i)
                  control_points[i] = Transformation.top() * (control_points[i] + k3d::vector3(cx, cy, 0));

          k3d::nurbs_curve::add_curve(Mesh, Primitive, 3, control_points, weights, knots);
  }

  static void parse_ellipse(const k3d::xml::element& SVG, transform_stack& Transformation, k3d::mesh& Mesh, k3d::nurbs_curve::primitive& Primitive)
  {
          const k3d::double_t cx = k3d::xml::attribute_value<k3d::double_t>(SVG, "cx", 0);
          const k3d::double_t cy = k3d::xml::attribute_value<k3d::double_t>(SVG, "cy", 0);
          const k3d::double_t rx = k3d::xml::attribute_value<k3d::double_t>(SVG, "rx", 1);
          const k3d::double_t ry = k3d::xml::attribute_value<k3d::double_t>(SVG, "ry", 1);

          k3d::mesh::knots_t knots;
          k3d::mesh::weights_t weights;
          k3d::mesh::points_t control_points;
          k3d::nurbs_curve::circular_arc(k3d::vector3(rx, 0, 0), k3d::vector3(0, ry, 0), 0, k3d::pi_times_2(), 4, knots, weights, control_points);

          for(k3d::uint_t i = 0; i != control_points.size(); ++i)
                  control_points[i] = Transformation.top() * (control_points[i] + k3d::vector3(cx, cy, 0));

          k3d::nurbs_curve::add_curve(Mesh, Primitive, 3, control_points, weights, knots);
  }

  static void parse_polyline(const k3d::xml::element& SVG, transform_stack& Transformation, k3d::mesh& Mesh, k3d::nurbs_curve::primitive& Primitive)
  {
          k3d::mesh::points_t control_points;

          std::istringstream point_stream(k3d::xml::attribute_text(SVG, "points"));
          for(k3d::point2 point; ;)
          {
                  point_stream >> point[0];
                  point_stream.ignore(1);
                  point_stream >> point[1];

                  if(!point_stream)
                          break;

                  control_points.push_back(Transformation.top() * k3d::point3(point[0], point[1], 0));
          }

          if(control_points.size() < 2)
                  return;

          k3d::nurbs_curve::add_curve(Mesh, Primitive, 2, control_points);
  }

  static void parse_polygon(const k3d::xml::element& SVG, transform_stack& Transformation, k3d::mesh& Mesh, k3d::nurbs_curve::primitive& Primitive)
  {
          k3d::mesh::points_t control_points;

          std::istringstream point_stream(k3d::xml::attribute_text(SVG, "points"));
          for(k3d::point2 point; ;)
          {
                  point_stream >> point[0];
                  point_stream.ignore(1);
                  point_stream >> point[1];

                  if(!point_stream)
                          break;

                  control_points.push_back(Transformation.top() * k3d::point3(point[0], point[1], 0));
          }

          if(control_points.size() < 2)
                  return;

          k3d::nurbs_curve::add_curve(Mesh, Primitive, 2, control_points, 1);
  }

  /*
  static void create_arc(k3d::point4 p1, k3d::point4 p2, k3d::double_t phi, k3d::double_t& cx, k3d::double_t&cy,
                  k3d::double_t rx, k3d::double_t ry, bool large_arc_flag, bool sweep_flag, int segments,
                  std::vector<k3d::point3>& control_points, k3d::mesh::knots_t& knots, k3d::mesh::weights_t& weights)
  {
          k3d::matrix4 T = k3d::identity3();
          k3d::point4 xp, cp, c;
          k3d::vector3 v1, v2;
          k3d::double_t theta_one, theta_two, delta_theta;

          T[0][0] = cos(phi);
          T[0][1] = sin(phi);
          T[1][0] = -sin(phi);
          T[1][1] = cos(phi);

          xp = T * k3d::to_point((p1 - p2) / 2);

          cp = std::sqrt(std::abs(rx * rx * ry * ry - rx * rx * xp[1] * xp[1] - ry * ry * xp[0] * xp[0]) / (rx * rx * xp[1] * xp[1] + ry * ry * xp[0] * xp[0])) * k3d::point4((rx / ry) * xp[1], (-ry / rx) * xp[0], 0, 1);

          if (large_arc_flag == sweep_flag)
                  cp = -cp;

          cp[3] = 1;

          T[0][0] = cos(phi);
          T[0][1] = -sin(phi);
          T[1][0] = sin(phi);
          T[1][1] = cos(phi);

          c = T * cp + (p1 + p2) / 2;

          v1 = k3d::vector3(1, 0, 0);
          v2 = k3d::vector3((xp[0] - cp[0]) / rx, (xp[1] - cp[1]) / ry, 0);

          theta_one = acos((v1 * v2) / (k3d::length(v1) * k3d::length(v2)));
          if (v1[0]*v2[1] - v1[1]*v2[0] < 0)
                  theta_one = -theta_one;

          v1 = v2;
          v2 = k3d::vector3((-xp[0] - cp[0]) / rx, (-xp[1] - cp[1]) / ry, 0);
          delta_theta = k3d::radians((int)k3d::degrees((acos((v1 * v2) / (k3d::length(v1) * k3d::length(v2))))) % 360);

          if (sweep_flag == large_arc_flag)
                  delta_theta = -delta_theta;

          theta_one += k3d::pi_times_2();
          if (large_arc_flag == 0)
                  theta_two = theta_one + delta_theta;
          else
                  if (sweep_flag == 0)
                  {
                          theta_two = theta_one + k3d::pi_times_2() - delta_theta;
                          rx = -rx;
                  }
                  else
                          theta_two = theta_one + k3d::pi_times_2() +  delta_theta;


          k3d::nurbs_curve::circular_arc(k3d::point3(rx, 0, 0), k3d::point3(0, ry, 0), theta_one, theta_two, segments, knots, weights, control_points);
          if (theta_two < theta_one)
          {
                  std::vector<k3d::point3> tmp_cp;
                  for (int i = 0; i < control_points.size(); i++)
                          tmp_cp.push_back(control_points[i]);
                  for (int i = 0; i < control_points.size(); i++)
                          control_points[i] = tmp_cp[control_points.size()-1-i];
          }
          cx = c[0];
          cy = c[1];
          for (int i = 0; i < control_points.size(); i++)
          {
                  k3d::point4 tmp(control_points[i][0], control_points[i][1], control_points[i][2], 1);
                  tmp = T * tmp;
                  control_points[i] = k3d::point3(tmp[0], tmp[1], tmp[2]);

          }
  }
  */

  static void parse_path(const k3d::xml::element& SVG, transform_stack& Transformation, k3d::mesh& Mesh, k3d::nurbs_curve::primitive& Primitive)
  {
          // Setup curves ...
          k3d::mesh::points_t points;
          k3d::mesh::orders_t orders;
          k3d::mesh::counts_t control_point_counts;
          k3d::mesh::indices_t control_points;
          k3d::mesh::weights_t control_point_weights;
          k3d::mesh::knots_t knots;

          std::stringstream data(k3d::xml::attribute_text(SVG, "d"));

          k3d::point3 current_point(0, 0, 0);
          for(char token = data.get(); data; token = data.get())
          {
                  k3d::bool_t relative = false;
                  switch(token)
                  {
                          case 'm':
                          {
                                  relative = true;
                          }
                          case 'M':
                          {
                                  k3d::double_t x, y = 0;
                                  get_pair(data, x, y);
                                  current_point[0] = relative ? current_point[0] + x : x;
                                  current_point[1] = relative ? current_point[1] + y : y;

                                  points.push_back(current_point);
                                  break;
                          }

                          case 'l':
                          {
                                  relative = true;
                          }
                          case 'L':
                          {
                                  k3d::double_t x, y = 0;
                                  get_pair(data, x, y);
                                  current_point[0] = relative ? current_point[0] + x : x;
                                  current_point[1] = relative ? current_point[1] + y : y;

                                  points.push_back(current_point);

                                  orders.push_back(2);
                                  control_point_counts.push_back(2);
                                  k3d::nurbs_curve::add_open_uniform_knots(2, 2, knots);
                                  control_points.push_back(points.size() - 2);
                                  control_points.push_back(points.size() - 1);
                                  control_point_weights.push_back(1);
                                  control_point_weights.push_back(1);

                                  break;
                          }

                          case 'h':
                          {
                                  relative = true;
                          }
                          case 'H':
                          {
                                  k3d::double_t x = 0;
                                  data >> x;
                                  current_point[0] = relative ? current_point[0] + x : x;

                                  points.push_back(current_point);

                                  orders.push_back(2);
                                  control_point_counts.push_back(2);
                                  k3d::nurbs_curve::add_open_uniform_knots(2, 2, knots);
                                  control_points.push_back(points.size() - 2);
                                  control_points.push_back(points.size() - 1);
                                  control_point_weights.push_back(1);
                                  control_point_weights.push_back(1);

                                  break;
                          }

                          case 'v':
                          {
                                  relative = true;
                          }
                          case 'V':
                          {
                                  k3d::double_t y = 0;
                                  data >> y;
                                  current_point[1] = relative ? current_point[1] + y : y;

                                  points.push_back(current_point);

                                  orders.push_back(2);
                                  control_point_counts.push_back(2);
                                  k3d::nurbs_curve::add_open_uniform_knots(2, 2, knots);
                                  control_points.push_back(points.size() - 2);
                                  control_points.push_back(points.size() - 1);
                                  control_point_weights.insert(control_point_weights.end(), 2, 1);

                                  break;
                          }

                          case 'c':
                          {
                                  relative = true;
                          }
                          case 'C':
                          {
                                  const k3d::point3 first_point = current_point;
                                  for(int i = 0; i != 3; ++i)
                                  {
                                          k3d::double_t x, y = 0;
                                          get_pair(data, x, y);
                                          current_point[0] = relative ? first_point[0] + x : x;
                                          current_point[1] = relative ? first_point[1] + y : y;

                                          points.push_back(current_point);

                                  }

                                  orders.push_back(4);
                                  control_point_counts.push_back(4);
                                  k3d::nurbs_curve::add_open_uniform_knots(4, 4, knots);
                                  control_points.push_back(points.size() - 4);
                                  control_points.push_back(points.size() - 3);
                                  control_points.push_back(points.size() - 2);
                                  control_points.push_back(points.size() - 1);
                                  control_point_weights.insert(control_point_weights.end(), 4, 1);

                                  break;
                          }

                          case 's':
                          {
                                  relative = true;
                          }
                          case 'S':
                          {
                                  const k3d::point3 first_point = current_point;

                                  const k3d::point3 previous_point = points[points.size() - 2];
                                  current_point[0] = first_point[0] + (first_point[0] - previous_point[0]);
                                  current_point[1] = first_point[1] + (first_point[1] - previous_point[1]);

                                  points.push_back(current_point);

                                  for(int i = 0; i != 2; ++i)
                                  {
                                          k3d::double_t x, y = 0;
                                          get_pair(data, x, y);
                                          current_point[0] = relative ? first_point[0] + x : x;
                                          current_point[1] = relative ? first_point[1] + y : y;

                                          points.push_back(current_point);

                                  }

                                  orders.push_back(4);
                                  control_point_counts.push_back(4);
                                  k3d::nurbs_curve::add_open_uniform_knots(4, 4, knots);
                                  control_points.push_back(points.size() - 4);
                                  control_points.push_back(points.size() - 3);
                                  control_points.push_back(points.size() - 2);
                                  control_points.push_back(points.size() - 1);
                                  control_point_weights.insert(control_point_weights.end(), 4, 1);

                                  break;
                          }

                          case 'q':
                          {
                                  relative = true;
                          }
                          case 'Q':
                          {
                                  const k3d::point3 first_point = current_point;
                                  for(int i = 0; i != 2; ++i)
                                  {
                                          k3d::double_t x, y = 0;
                                          get_pair(data, x, y);
                                          current_point[0] = relative ? first_point[0] + x : x;
                                          current_point[1] = relative ? first_point[1] + y : y;

                                          points.push_back(current_point);

                                  }

                                  orders.push_back(3);
                                  control_point_counts.push_back(3);
                                  k3d::nurbs_curve::add_open_uniform_knots(3, 3, knots);
                                  control_points.push_back(points.size() - 3);
                                  control_points.push_back(points.size() - 2);
                                  control_points.push_back(points.size() - 1);
                                  control_point_weights.insert(control_point_weights.end(), 3, 1);

                                  break;
                          }

                          case 't':
                          {
                                  relative = true;
                          }
                          case 'T':
                          {
                                  const k3d::point3 first_point = current_point;

                                  const k3d::point3 previous_point = points[points.size() - 2];
                                  current_point[0] = first_point[0] + (first_point[0] - previous_point[0]);
                                  current_point[1] = first_point[1] + (first_point[1] - previous_point[1]);

                                  points.push_back(current_point);

                                  for(int i = 0; i != 1; ++i)
                                  {
                                          k3d::double_t x, y = 0;
                                          get_pair(data, x, y);
                                          current_point[0] = relative ? first_point[0] + x : x;
                                          current_point[1] = relative ? first_point[1] + y : y;

                                          points.push_back(current_point);

                                  }

                                  orders.push_back(3);
                                  control_point_counts.push_back(3);
                                  k3d::nurbs_curve::add_open_uniform_knots(3, 3, knots);
                                  control_points.push_back(points.size() - 3);
                                  control_points.push_back(points.size() - 2);
                                  control_points.push_back(points.size() - 1);
                                  control_point_weights.insert(control_point_weights.end(), 3, 1);

                                  break;
                          }
                          case 'z':
                          case 'Z':
                          {
                                  orders.push_back(2);
                                  control_point_counts.push_back(2);
                                  k3d::nurbs_curve::add_open_uniform_knots(2, 2, knots);
                                  control_points.push_back(points.size() - 1);
                                  control_points.push_back(0);
                                  control_point_weights.push_back(1);
                                  control_point_weights.push_back(1);

                                  break;
                          }
                  }

  /*
                          //Arc: creates an arc from current point to (x,y) with the semiaxes radius given by (rx,ry)
                          //     fa and fs: are used to choose from the possible 4 arcs created by the two given points
                          //     and the given radius
                          //     phi is the angle of the x-axis of the ellipse relative to the current x-axis
                  case 'a':
                          relative = true;
                  case 'A':
                          order = 3;
                          std::vector<k3d::point3> control_points;
                          k3d::mesh::knots_t tmp_knots;
                          k3d::mesh::weights_t tmp_weights;
                          k3d::double_t cx, cy, rx, ry, phi, fa, fs;
                          k3d::point4 p1 = k3d::point4(x, y, 0, 1);
                          get_pair(rx, ry, data);
                          data >> phi;
                          get_pair(fa, fs, data);
                          get_pair(x, y, data);
                          if (relative)
                          {
                                  x += lastpoint[0];
                                  y += lastpoint[1];
                          }
                          create_arc(p1, k3d::point4(x, y, 0, 1), k3d::radians(phi), cx, cy, rx, ry, fa, fs, 8, control_points, knots, tmp_weights);
                          return_if_fail(tmp_weights.size() == control_points.size());
                          for (int i = 1; i < control_points.size(); i++)
                          {
                                  points.push_back(count);
                                  count++;
                                  add_point(k3d::point4(control_points[i][0] + cx, control_points[i][1] + cy, control_points[i][2], 1));
                          }
                          is_arc = true;
                          k3d::point3 t_point = control_points.at(control_points.size() - 2);
                          slastpoint = k3d::point4(t_point[0], t_point[1], t_point[2], 1);
                          t_point = control_points.back();
                          lastpoint = k3d::point4(t_point[0], t_point[1], t_point[2], 1);
                          last = points.back();
                          break;
                  }

                  if (!is_arc)
                  {
                          //Start knot Vector Definition to be a Bezier curve
                          //knots.push_back(0);
                          //for(int i=0; i < order-1; i++)
                          //      knots.push_back(1);
                          //for(int i=0; i < order-1; i++)
                          //      knots.push_back(2);
                          //knots.push_back(3);
                          knots.push_back(knots.back());
                          knots.push_back(knots.back());
                          knots.push_back(knots.back() + 1);
                          knots.push_back(knots.back());
                          //End knot vector definition

                  }
          }

          knots.push_back(knots.back());
          knots.push_back(knots.back());

          for (k3d::uint_t point = 0; point < points.size(); point++)
                  weights.push_back(1.0);

          k3d::nurbs_curve::add_curve(order, points, weights, knots, Mesh, Primitive);
          relative = false;
          is_arc = false;
  */
          }

          k3d::nurbs_curve::add_curves(Mesh, Primitive, points, orders, control_point_counts, control_points, control_point_weights, knots);
  }

  /// Main parsing function, doesn't handle grouping and just traverse the XML tree recursively
  static void parse_graphics(const k3d::xml::element& SVG, transform_stack& Transformation, k3d::mesh& Mesh, k3d::nurbs_curve::primitive& Primitive)
  {
          k3d::mesh::colors_t colors;
          for(k3d::xml::element::elements_t::const_iterator svg = SVG.children.begin(); svg != SVG.children.end(); ++svg)
          {
                  const k3d::string_t trans = k3d::xml::attribute_text(*svg, "transform", "none");
                  //If there is a transformation extract matrix from data and push into stack
                  if (trans != "none")
                  {
                          k3d::matrix4 mat = k3d::identity3();
                          std::istringstream trans_stream(trans);
                          k3d::double_t x, y;
                          char token;
                          //char tmp;
                          while (!trans_stream.eof())
                          {
                                  trans_stream >> token;
                                  k3d::matrix4 tmp_mat = k3d::identity3();
                                  switch (token)
                                  {
                                          //Set up translation matrix
                                          // (( 1  0 0 0)
                                          //  ( 0  1 0 0)
                                          //  ( 0  0 1 0)
                                          //  (tx ty 0 1))
                                          case 't':
                                                  trans_stream.ignore(9);
                                                  get_pair(trans_stream, x, y);
                                                  tmp_mat[0][3] = x;
                                                  tmp_mat[1][3] = y;
                                                  break;

                                          //Set up rotation matrix
                                          // ((cos(t) -sin(t) 0 0)
                                          //  (sin(t)  cos(t) 0 0)
                                          //  (     0       0 1 0)
                                          //  (     0       0 0 1))
                                          case 'r':
                                                  trans_stream.ignore(6);
                                                  trans_stream >> x;
                                                  x = k3d::radians(x);
                                                  tmp_mat[0][0] = cos(x);
                                                  tmp_mat[0][1] = -sin(x);
                                                  tmp_mat[1][0] = sin(x);
                                                  tmp_mat[1][1] = cos(x);
                                                  break;

                                          //Set up scaling matrix
                                          // ((cx 0 0 0)
                                          //  (0 cy 0 0)
                                          //  (0  0 1 0)
                                          //  (0  0 0 1))
                                          case 's':
                                                  trans_stream >> token;
                                                  if (token == 'k')
                                                  {
                                                          trans_stream.ignore(2);
                                                          trans_stream >> token;
                                                          trans_stream.ignore();
                                                          trans_stream >> x;
                                                          if (token == 'X')
                                                                  tmp_mat[0][1] = tan(k3d::radians(x));
                                                          else
                                                                  tmp_mat[1][0] = tan(k3d::radians(x));
                                                  }
                                                  else
                                                  {
                                                          trans_stream.ignore(4);
                                                          trans_stream >> x;
                                                          tmp_mat[0][0] = x;
                                                          tmp_mat[1][1] = x;
                                                          break;
                                                  }
                                                  break;

                                          //Set up custom matrix
                                          // ((a00 a01 0 0)
                                          //  (a10 a11 0 0)
                                          //  (  0   0 1 0)
                                          //  (a30 a31 0 1))
                                          case 'm':
                                                  trans_stream.ignore(6);
                                                  trans_stream >> tmp_mat[0][0];
                                                  trans_stream.ignore();
                                                  trans_stream >> tmp_mat[1][0];
                                                  trans_stream.ignore();
                                                  trans_stream >> tmp_mat[0][1];
                                                  trans_stream.ignore();
                                                  trans_stream >> tmp_mat[1][1];
                                                  trans_stream.ignore();
                                                  trans_stream >> tmp_mat[0][3];
                                                  trans_stream.ignore();
                                                  trans_stream >> tmp_mat[1][3];
                                                  trans_stream.ignore();
                                  }

                                  trans_stream.ignore(2);
                                  mat = mat * tmp_mat;
                          }
                          //Push the generated matrix into the stack relative to the one in the top
                          Transformation.push(Transformation.top() * mat);
                  }

                  if(svg->name == "g")
                          parse_graphics(*svg, Transformation, Mesh, Primitive);
                  if(svg->name == "line")
                          parse_line(*svg, Transformation, Mesh, Primitive);
                  if(svg->name == "rect")
                          parse_rect(*svg, Transformation, Mesh, Primitive);
                  if(svg->name == "circle")
                          parse_circle(*svg, Transformation, Mesh, Primitive);
                  if(svg->name == "ellipse")
                          parse_ellipse(*svg, Transformation, Mesh, Primitive);
                  if(svg->name == "polyline")
                          parse_polyline(*svg, Transformation, Mesh, Primitive);
                  if(svg->name == "polygon")
                          parse_polygon(*svg, Transformation, Mesh, Primitive);
                  if(svg->name == "path")
                          parse_path(*svg, Transformation, Mesh, Primitive);

                  k3d::color color;
                  if(get_color(*svg, color))
                  {
                          colors.resize(Primitive.curve_first_points.size(), color);
                  }

                  if(trans != "none")
                          Transformation.pop();
          }
          if(colors.size() == Primitive.curve_first_points.size())
                  Primitive.curve_attributes.create("Cs", new k3d::mesh::colors_t(colors));
  }
}


#endif /* SVG_PARSING_H_ */
